package fox.framework.rpc.context.event;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author cuichao
 * @Description: DefaultRpcEventMulticaster
 * @Date: create in 2021/2/18 20:34
 */
public class DefaultRpcEventMulticaster implements RpcEventMulticaster {

    private Map<Class<? extends RpcEvent>, Set<RpcEventListener>> listenerMap = new ConcurrentHashMap();
    private Lock lock = new ReentrantLock();

    @Override
    public void addEventListener(RpcEventListener listener) {
        Class eventClass = findRpcEventClass(listener.getClass());
        if (eventClass != null) {
            lock.lock();
            try {
                if (!listenerMap.containsKey(eventClass)) {
                    Set<RpcEventListener> set = new HashSet<>();
                    set.add(listener);
                    listenerMap.put(eventClass, set);
                } else {
                    listenerMap.get(eventClass).add(listener);
                }
            } finally {
                lock.unlock();
            }
        }
    }

    @Override
    public void removeEventListener(RpcEventListener listener) {
        Class eventClass = findRpcEventClass(listener.getClass());
        lock.lock();
        try {
            if(listenerMap.containsKey(eventClass)){
                Set<RpcEventListener> listenerSet = listenerMap.get(eventClass);
                listenerSet.remove(listener);
            }
        } finally {
            lock.unlock();
        }

    }

    @Override
    public void publishEvent(RpcEvent event) {
        if (listenerMap.containsKey(event.getClass())) {
            Set<RpcEventListener> eventListeners = listenerMap.get(event.getClass());
            for (RpcEventListener listener : eventListeners) {
                listener.onEvent(event);
            }
        }
    }

    @Override
    public void clear() {
        lock.lock();
        try {
            listenerMap.clear();
        }finally {
            lock.unlock();
        }
    }

    /**
     * 根据传入class 查找其继承RpcEvent泛型类
     * @param source
     * @return
     */
    private Class findRpcEventClass(Class source) {
        Class superclass = source.getSuperclass();
        Class[] interfaces = source.getInterfaces();

        if (superclass != null && superclass == RpcEventListener.class) {
            Type genericSuperclass = source.getGenericSuperclass();
            if (genericSuperclass instanceof ParameterizedType) {
                Type[] typeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
                for (Type typeArgument : typeArguments) {
                    try {
                        Class cls = findRpcEventClass(typeArgument);
                        if (cls == null) {
                            continue;
                        }
                        return cls;
                    } catch (ClassNotFoundException e) {
                        //nothing to do
                    }
                }
            }
        }
        if (superclass != null && RpcEventListener.class.isAssignableFrom(superclass)) {
            return findRpcEventClass(superclass);
        }
        for (Class inter : interfaces) {
            if (RpcEventListener.class == inter) {
                Type[] genericInterfaces = source.getGenericInterfaces();
                for (Type genInterface : genericInterfaces) {
                    if (genInterface instanceof ParameterizedType) {
                        Type[] typeArguments = ((ParameterizedType) genInterface).getActualTypeArguments();
                        for (Type typeArgument : typeArguments) {
                            try {
                                Class cls = findRpcEventClass(typeArgument);
                                if (cls == null) {
                                    continue;
                                }
                                return cls;
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }

                    }
                }
            }

            if (RpcEventListener.class.isAssignableFrom(inter)) {
                return findRpcEventClass(inter);
            }
        }

        return null;
    }


    private Class findRpcEventClass(Type type) throws ClassNotFoundException {
        Class<?> cls = Class.forName(type.getTypeName());
        if (RpcEvent.class.isAssignableFrom(cls)) {
            return cls;
        }
        return null;
    }
}
